// 8. 异常处理
/**
 * 参考链接:
 * 1. https://www.html.cn/softprog/c/130278.html
 * 2. https://blog.csdn.net/Trouble_provider/article/details/90521215
 * 3. https://www.cnblogs.com/dishengAndziyu/p/10920150.html
 * 
 * 1. try、catch、throw使用
 *      try{
 *        throw "Error: test error!";
 *      }catch(type &obje){
 *        cout << obje.what();
 *      }catch(char *msg){
 *        cout << msg;
 *      }
 * 
 * 2. catch 块处理try块抛出的任意类型,使用省略号...
 *      语法: catch(...){}
 *      注意: 该语句块要放在其他catch块下面，及最底下
 * 
 * 3. 内置标准异常
 *    异常	                描述
      std::exception	      该异常是所有标准 C++ 异常的父类。
      std::bad_alloc	      该异常可以通过 new 抛出。
      std::bad_cast	        该异常可以通过 dynamic_cast 抛出。
      std::bad_exception	  这在处理 C++ 程序中无法预期的异常时非常有用。
      std::bad_typeid	      该异常可以通过 typeid 抛出。
      std::logic_error	    理论上可以通过**读取代码**来检测到的异常。
      std::domain_error	    当使用了一个**无效的数学域**时，会抛出该异常。
      std::invalid_argument	当使用了无效的参数时，会抛出该异常。
      std::length_error	    当创建了太长的 std::string 时，会抛出该异常。
      std::out_of_range	    该异常可以通过方法抛出，例如 std::vector 和 std::bitset<>::operator[]()。
      std::runtime_error	  理论上不可以通过读取代码来检测到的异常。
      std::overflow_error	  当发生数学上溢时，会抛出该异常。
      std::range_error	    当尝试存储超出范围的值时，会抛出该异常。
      std::underflow_error	当发生数学下溢时，会抛出该异常。
 * 4. 自定义异常处理类型类
      继承std::exception,重写基类虚方法what
 *
 * 5. 异常规格说明
 *    语法：return_type fun() throw (err_type1,err_type2,...){}
 *    说明: 
 *          return_type fun() throw(int,char *){} 指的是函数只能抛出int和char*型异常
 *          return_type fun() throw(){} 或者
 *  return_type fun() noexcept {} 指的是函数不能抛出任何异常
 *          return_type fun() {}  不指定，即函数可以抛出任意类型异常
 *    #51注意: 当函数抛出的异常不在规格说明中时，全局函数unexcepted()被调用，默认的unexcepted()函数会调用,可以使用set_unexcepted(void (*)())指定自定义未知异常类型处理函数。该自定义函数内部必须抛出在异常说明中的类型错误，否则还是会接着调terminate终止程序运行。
 *
 * 扩展:
 * 1. 函数默认值，类中函数设置默认值
 * 2. 函数名后面的const
 * 3. mutable的使用
*/

#include <iostream>

#define __mark 51

#if 51 == __mark

/**
 * #51 设置unexcepted
*/
void fun(int) throw(int, char, char *);
void myUnexcepted()
{
  std::cout << "myUnexcepted()..." << std::endl;
  // terminate
  // throw 1.0; // terminate
}
int main(void)
{
  std::set_unexpected(myUnexcepted);
  try
  {
    fun(4);
  }
  catch (std::exception &obj)
  {
    std::cout << obj.what() << std::endl;
  }
  catch (int e)
  {
    std::cout << e << std::endl;
  }
  catch (char e)
  {
    std::cout << e << std::endl;
  }
  catch (char *e)
  {
    std::cout << e << std::endl;
  }

  printf("-----------end-------------\n");
  return 0;
}
void fun(int i) throw(int, char, char *)
{
  if (0 == i)
  {
    throw 1;
  }
  else if (1 == i)
  {
    throw 'a';
  }
  else if (2 == i)
  {
    throw "Error: test error!";
  }
  else
  {
    throw std::out_of_range("Error: out of reange");
  }
}

#elif 2 == __mark
#endif
